package com.zhouzj.task.zookeeper.provider.handler;

import cn.hutool.core.map.MapUtil;
import com.zhouzj.task.zookeeper.common.annotions.RpcService;
import com.zhouzj.task.zookeeper.common.consist.Beat;
import com.zhouzj.task.zookeeper.common.rpc.RpcRequest;
import com.zhouzj.task.zookeeper.common.rpc.RpcResponse;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.springframework.beans.BeansException;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author: zhouzhijun
 * @Date: 2021/7/4 17:40
 */
@ChannelHandler.Sharable
@Component
public class RpcServerHandler extends SimpleChannelInboundHandler<RpcRequest> implements ApplicationContextAware {

    private static final Map<String, Object> SERVICE_INSTANCE_MAP = new ConcurrentHashMap<>();

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, RpcRequest rpcRequest) throws Exception {
        if (Beat.BEAT_ID.equalsIgnoreCase(rpcRequest.getRequestId())) {
            System.out.println("===idle===");
            return;
        }
        RpcResponse response = new RpcResponse();
        response.setRequestId(rpcRequest.getRequestId());
        try {
            response.setResult(handler(rpcRequest));
        } catch (Exception e) {
            e.printStackTrace();
            response.setSuccess(false);
            response.setError(e.getMessage());
        }
        channelHandlerContext.writeAndFlush(response);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, Object> serviceBeanMap = applicationContext.getBeansWithAnnotation(RpcService.class);
        if (MapUtil.isNotEmpty(serviceBeanMap)) {
            Set<Map.Entry<String, Object>> entries = serviceBeanMap.entrySet();
            for (Map.Entry<String, Object> item : entries) {
                Object serviceBean = item.getValue();
                if (serviceBean.getClass().getInterfaces().length == 0) {
                    throw new RuntimeException("service must implements interface.");
                }
                String interfaceName = serviceBean.getClass().getInterfaces()[0].getName();
                SERVICE_INSTANCE_MAP.put(interfaceName, serviceBean);
            }
        }
    }

    /**
     * 业务逻辑处理
     *
     * @param request
     * @return
     * @throws InvocationTargetException
     */
    private Object handler(RpcRequest request) throws InvocationTargetException {

        Object serviceBean = SERVICE_INSTANCE_MAP.get(request.getClassName());

        Class<?> serviceClass = serviceBean.getClass();

        String methodName = request.getMethodName();

        Class<?>[] parameterTypes = request.getParameterTypes();
        Object[] parameters = request.getParameters();

        //使用CGLB Reflect
        FastClass fastClass = FastClass.create(serviceClass);
        FastMethod fastMethod = fastClass.getMethod(methodName, parameterTypes);

        return fastMethod.invoke(serviceBean, parameters);
    }
}
